package com.firefly.admin.common.utils;

import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.firefly.admin.common.enums.ResultCodeEnum;
import com.firefly.admin.common.exception.BusinessException;
import com.firefly.admin.tripartite.entity.FyTripartiteWechatMerchant;
import com.firefly.admin.tripartite.service.FyTripartiteWechatMerchantService;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;

@Component
public class WeChatPayUtil {

    private static final String QR_IMAGE_TYPE = "jpg";

    @Autowired
    private FyTripartiteWechatMerchantService service;
    private static FyTripartiteWechatMerchantService fyTripartiteWechatMerchantConfigService;

    @PostConstruct
    public void init() {
        fyTripartiteWechatMerchantConfigService = this.service;
    }

    public static String nativePay(String attach, String appId, String outTradeNo, Integer totalFee, String notifyUrl) {
        FyTripartiteWechatMerchant tripartiteWechatMerchantConfig = fyTripartiteWechatMerchantConfigService.findByAppId(appId);
        String merchantId = tripartiteWechatMerchantConfig.getMerchantId();
        PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(tripartiteWechatMerchantConfig.getMerchantKey());
        Config config = (new RSAAutoCertificateConfig.Builder())
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(tripartiteWechatMerchantConfig.getMerchantSerialNumber())
                .apiV3Key(tripartiteWechatMerchantConfig.getApiV3Key()).build();
        NativePayService nativePayService = (new NativePayService.Builder()).config(config).build();
        com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new
                com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
        com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription("微信支付");
        request.setAttach(attach);
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(outTradeNo);
        com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = nativePayService.prepay(request);
        if (response == null) {
            throw new BusinessException(ResultCodeEnum.WECHAT_PAY_CREATE_ORDER_ERROR.getMsg());
        } else {
            QrConfig qrConfig = new QrConfig(300, 300);
            return QrCodeUtil.generateAsBase64(response.getCodeUrl(), qrConfig, QR_IMAGE_TYPE);
        }
    }

    public static String jsApiPay(String attach, String appId, String outTradeNo, Integer totalFee, String openId, String notifyUrl) {
        FyTripartiteWechatMerchant tripartiteWechatMerchantConfig = fyTripartiteWechatMerchantConfigService.findByAppId(appId);
        String merchantId = tripartiteWechatMerchantConfig.getMerchantId();
        PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(tripartiteWechatMerchantConfig.getMerchantKey());
        Config config = (new RSAAutoCertificateConfig.Builder())
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(tripartiteWechatMerchantConfig.getMerchantSerialNumber())
                .apiV3Key(tripartiteWechatMerchantConfig.getApiV3Key()).build();
        JsapiService service = (new JsapiService.Builder()).config(config).build();
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription("微信支付");
        request.setAttach(attach);
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(outTradeNo);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        PrepayResponse response = service.prepay(request);
        if (response == null) {
            throw new BusinessException(ResultCodeEnum.WECHAT_PAY_CREATE_ORDER_ERROR.getMsg());
        } else {
            return response.getPrepayId();
        }
    }

    public static Transaction weChatCallback(HttpServletRequest request) {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String singType = request.getHeader("Wechatpay-Signature-Type");
        String merchantSerialNumber = request.getHeader("Wechatpay-Serial");
        String body = getRequestBody(request);
        FyTripartiteWechatMerchant tripartiteWechatMerchantConfig = fyTripartiteWechatMerchantConfigService.findByMerchantSerialNumber(merchantSerialNumber);
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(tripartiteWechatMerchantConfig.getMerchantId())
                .privateKey(tripartiteWechatMerchantConfig.getMerchantKey())
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(tripartiteWechatMerchantConfig.getApiV3Key())
                .build();
        NotificationParser parser = new NotificationParser(config);
        RequestParam requestParam = new RequestParam.Builder().nonce(nonce).signature(signature).signType(singType)
                .timestamp(timestamp).serialNumber(merchantSerialNumber).body(body).build();
        return parser.parse(requestParam, Transaction.class);
    }

    private static String getRequestBody(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static String createSign(String appId, String currentTimeStamp, String nonceStr, String prepayId, String merchantKey) throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append(appId).append("\n");
        sb.append(currentTimeStamp).append("\n");
        sb.append(nonceStr).append("\n");
        sb.append("prepay_id=").append(prepayId).append("\n");
        PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(merchantKey);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(sb.toString().getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public static void queryPayOrder(String outTradeNo, String mchId, String merchantKey, String merchantSerialNo, String apiKey) {
        Config config = (new RSAAutoCertificateConfig.Builder()).merchantId(mchId).privateKey(merchantKey).merchantSerialNumber(merchantSerialNo).apiV3Key(apiKey).build();
        JsapiService service = (new JsapiService.Builder()).config(config).build();
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(outTradeNo);
        request.setMchid(mchId);
        Transaction transaction = service.queryOrderByOutTradeNo(request);
        if (transaction == null) {
            throw new BusinessException(ResultCodeEnum.WECHAT_PAY_CREATE_ORDER_ERROR.getMsg());
        } else if (!"SUCCESS".equals(transaction.getTradeState().name())) {
            throw new BusinessException(ResultCodeEnum.WECHAT_PAY_ORDER_UNPAID.getMsg());
        }
    }

    public static Refund refund(String transactionId, String mchId, String outTradeNo, Long totalFee, Long refund, String reason, String merchantKey, String merchantSerialNo, String apiKey, String reFundNo) {
        Config config = (new RSAAutoCertificateConfig.Builder()).merchantId(mchId).privateKey(merchantKey).merchantSerialNumber(merchantSerialNo).apiV3Key(apiKey).build();
        RefundService refundService = (new RefundService.Builder()).config(config).build();
        AmountReq amount = new AmountReq();
        amount.setRefund(refund);
        amount.setTotal(totalFee);
        amount.setCurrency("CNY");
        CreateRequest createRequest = new CreateRequest();
        createRequest.setAmount(amount);
        createRequest.setTransactionId(transactionId);
        createRequest.setOutRefundNo(reFundNo);
        createRequest.setOutTradeNo(outTradeNo);
        createRequest.setReason(reason);
        createRequest.setNotifyUrl("");
        return refundService.create(createRequest);
    }
}
